From verion 0.4.10 of the circlize package, there is a new group argument in chordDiagram() function which is very convenient for making multiple-group Chord diagrams.
I first generate a random matrix where there are three groups (A, B, and C). Note this new functionality works the same for the input as a data frame.
library(circlize)
mat1 = matrix(rnorm(25), nrow = 5)
rownames(mat1) = paste0("A", 1:5)
colnames(mat1) = paste0("B", 1:5)
mat2 = matrix(rnorm(25), nrow = 5)
rownames(mat2) = paste0("A", 1:5)
colnames(mat2) = paste0("C", 1:5)
mat3 = matrix(rnorm(25), nrow = 5)
rownames(mat3) = paste0("B", 1:5)
colnames(mat3) = paste0("C", 1:5)
mat = matrix(0, nrow = 10, ncol = 10)
rownames(mat) = c(rownames(mat2), rownames(mat3))
colnames(mat) = c(colnames(mat1), colnames(mat2))
mat[rownames(mat1), colnames(mat1)] = mat1
mat[rownames(mat2), colnames(mat2)] = mat2
mat[rownames(mat3), colnames(mat3)] = mat3
mat
## B1 B2 B3 B4 B5 C1 C2 C3 C4 C5
## A1 0.1076 -0.11 -0.521 0.45 0.92 -2.59 1.15 -0.34 0.683 -1.855
## A2 0.4707 0.81 -0.092 0.12 1.04 0.53 -1.43 0.28 1.631 -0.381
## A3 0.0038 -0.10 0.470 -0.57 0.66 0.96 0.38 -1.84 -0.639 0.071
## A4 -0.3996 -1.06 -0.343 0.82 0.61 0.28 0.19 1.23 -0.074 1.017
## A5 0.6163 0.35 0.317 -0.86 -0.28 0.78 -0.21 0.54 0.794 0.387
## B1 0.0000 0.00 0.000 0.00 0.00 -1.31 0.40 -0.80 0.400 0.856
## B2 0.0000 0.00 0.000 0.00 0.00 1.30 -0.71 0.63 1.430 -0.309
## B3 0.0000 0.00 0.000 0.00 0.00 0.40 0.22 0.84 0.546 -0.836
## B4 0.0000 0.00 0.000 0.00 0.00 -0.18 -0.80 0.37 -1.424 1.371
## B5 0.0000 0.00 0.000 0.00 0.00 1.01 0.66 -0.65 0.530 1.824
The main thing is to create “a grouping variable”. The variable contains the group labels and the sector names are used as the names in the vector.
nm = unique(unlist(dimnames(mat)))
group = structure(gsub("\\d", "", nm), names = nm)
group
## A1 A2 A3 A4 A5 B1 B2 B3 B4 B5 C1 C2 C3 C4 C5
## "A" "A" "A" "A" "A" "B" "B" "B" "B" "B" "C" "C" "C" "C" "C"
Assign group variable to the group argument:
grid.col = structure(c(rep(2, 5), rep(3, 5), rep(4, 5)),
names = c(paste0("A", 1:5), paste0("B", 1:5), paste0("C", 1:5)))
chordDiagram(mat, group = group, grid.col = grid.col)
circos.clear()
We can try another grouping:
group = structure(gsub("^\\w", "", nm), names = nm)
group
## A1 A2 A3 A4 A5 B1 B2 B3 B4 B5 C1 C2 C3 C4 C5
## "1" "2" "3" "4" "5" "1" "2" "3" "4" "5" "1" "2" "3" "4" "5"
chordDiagram(mat, group = group, grid.col = grid.col)
circos.clear()
The order of group controls the sector orders and if group is set as a factor, the order of levels controls the order of groups.
group = structure(gsub("\\d", "", nm), names = nm)
group = factor(group[sample(length(group), length(group))], levels = c("C", "A", "B"))
group
## B1 A5 C5 A1 C3 C2 B2 B4 C1 A3 A4 C4 B3 A2 B5
## B A C A C C B B C A A C B A B
## Levels: C A B
chordDiagram(mat, group = group, grid.col = grid.col)
circos.clear()
The gap between groups is controlled by big.gap argument and the gap between sectors is controlled by small.gap argument.
group = structure(gsub("\\d", "", nm), names = nm)
chordDiagram(mat, group = group, grid.col = grid.col, big.gap = 20, small.gap = 5)
circos.clear()
As a normal Chord diagram, the labels and other tracks can be manually adjusted:
group = structure(gsub("\\d", "", nm), names = nm)
chordDiagram(mat, group = group, grid.col = grid.col,
annotationTrack = c("grid", "axis"),
preAllocateTracks = list(
track.height = mm_h(4),
track.margin = c(mm_h(4), 0)
))
circos.track(track.index = 2, panel.fun = function(x, y) {
sector.index = get.cell.meta.data("sector.index")
xlim = get.cell.meta.data("xlim")
ylim = get.cell.meta.data("ylim")
circos.text(mean(xlim), mean(ylim), sector.index, cex = 0.6, niceFacing = TRUE)
}, bg.border = NA)
highlight.sector(rownames(mat1), track.index = 1, col = "red",
text = "A", cex = 0.8, text.col = "white", niceFacing = TRUE)
highlight.sector(colnames(mat1), track.index = 1, col = "green",
text = "B", cex = 0.8, text.col = "white", niceFacing = TRUE)
highlight.sector(colnames(mat2), track.index = 1, col = "blue",
text = "C", cex = 0.8, text.col = "white", niceFacing = TRUE)
circos.clear()